]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Make startup animation use async functions throughout
authorWill Thompson <wjt@endlessos.org>
Fri, 19 Jul 2024 13:11:15 +0000 (14:11 +0100)
committerWill Thompson <wjt@endlessos.org>
Sun, 28 Jul 2024 17:49:59 +0000 (18:49 +0100)
During session startup, we have the following chain:

    LayoutManager._prepareStartupAnimation()
    `- LayoutManager._startupAnimation()
       `- LayoutManager._startupAnimationComplete()       (a)
       | or
       `- LayoutManager._startupAnimationGreeter()        (b)
       | or
       `- LayoutManager._startupAnimationSession()        (c)
          `- Overview.runStartupAnimation()               (d)
             `- OverviewActor.runStartupAnimation()       (e)
                `- ControlsManager.runStartupAnimation()  (f)

Branch (b) calls LayoutManager._startupAnimationComplete() once the
animation is complete. Branch (c) does the same, except that
ControlsManager.runStartupAnimation() is an async function that awaits
LayoutManager.ensureAllocation().

LayoutManager._prepareStartupAnimation() is an `async` function, and its
caller catches & logs any error it raises.

Previously, ControlsManager.runStartupAnimation() – (f) in the above
diagram – was declared `async`, because it uses `await` internally, but
also accepted a callback, which was called on successful completion of
the function and the animation it triggers.

If an exception is raised during execution of the function, its callback
would never be called, and because the promise it implicitly returns is
discarded, the exception would never be logged either. And, stepping a
few levels up the call stack, the callback that
LayoutManager._startupAnimationSession() (c) provided would never be
called, and so LayoutManager._startupAnimationComplete() would never be
called, meaning the cover pane that prevents input would never be
removed and the session would be unusable.

Remove the callback parameter from ControlsManager.runStartupAnimation()
(f), and instead make it resolve in the case where the callback would
previously have been called.

Remove the callback parameter from OverviewActor.runStartupAnimation()
(e) – it is just a wrapper around ControlsManager.runStartupAnimation().

Adjust Overview.runStartupAnimation() (d) accordingly: rather than
passing a callback to OverviewActor.runStartupAnimation(), await its
return and then proceed. There is a slight behaviour change here:
previously, in the branch where this._syncGrab() is false, the callback
would be called before calling this.hide(), whereas now this.hide() is
called before the async function returns.

Adjust LayoutManager._startupAnimationSession() and its siblings to be
async, rather than each calling _startupAnimationComplete() directly.
Finally, in _prepareStartupAnimation()  await _startupAnimation() either
succeeding or failing, and in either case call
_startupAnimationComplete(), removing the cover pane that prevents all
input.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3428>

js/ui/layout.js
js/ui/overview.js
js/ui/overviewControls.js

index 3b54960f84ef3f2ffa754a8f0704e6b268747b9c..5b09817ba3d0b5553adb78719deb3bffe76b40b6 100644 (file)
@@ -763,39 +763,47 @@ export const LayoutManager = GObject.registerClass({
         else
             this.emit('startup-prepared');
 
-        this._startupAnimation();
+        try {
+            await this._startupAnimation();
+        } finally {
+            this._startupAnimationComplete();
+        }
     }
 
-    _startupAnimation() {
+    async _startupAnimation() {
         if (Meta.is_restart())
-            this._startupAnimationComplete();
-        else if (Main.sessionMode.isGreeter)
-            this._startupAnimationGreeter();
+            return;
+
+        if (Main.sessionMode.isGreeter)
+            await this._startupAnimationGreeter();
         else
-            this._startupAnimationSession();
+            await this._startupAnimationSession();
     }
 
-    _startupAnimationGreeter() {
-        this.panelBox.ease({
-            translation_y: 0,
-            duration: STARTUP_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onStopped: () => this._startupAnimationComplete(),
+    async _startupAnimationGreeter() {
+        await new Promise(resolve => {
+            this.panelBox.ease({
+                translation_y: 0,
+                duration: STARTUP_ANIMATION_TIME,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onStopped: () => resolve(),
+            });
         });
     }
 
-    _startupAnimationSession() {
-        const onStopped = () => this._startupAnimationComplete();
+    async _startupAnimationSession() {
         if (Main.sessionMode.hasOverview) {
-            Main.overview.runStartupAnimation(onStopped);
+            await Main.overview.runStartupAnimation();
         } else {
-            this.uiGroup.ease({
-                scale_x: 1,
-                scale_y: 1,
-                opacity: 255,
-                duration: STARTUP_ANIMATION_TIME,
-                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-                onStopped,
+            await new Promise(resolve => {
+                this.uiGroup.ease({
+                    scale_x: 1,
+                    scale_y: 1,
+                    opacity: 255,
+                    duration: STARTUP_ANIMATION_TIME,
+                    mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                    onStopped: () => resolve(),
+                });
             });
         }
     }
index 7e431b68e32779d1ce4ac9c27702c337a26d59fe..2f40d2fd9f7f4291e11c903c7c0c1537cfbc46df 100644 (file)
@@ -90,8 +90,8 @@ class OverviewActor extends St.BoxLayout {
         this._controls.animateFromOverview(callback);
     }
 
-    runStartupAnimation(callback) {
-        this._controls.runStartupAnimation(callback);
+    async runStartupAnimation() {
+        await this._controls.runStartupAnimation();
     }
 
     get dash() {
@@ -666,7 +666,7 @@ export class Overview extends Signals.EventEmitter {
         this._overview.controls.appDisplay.selectApp(id);
     }
 
-    runStartupAnimation(callback) {
+    async runStartupAnimation() {
         Main.panel.style = 'transition-duration: 0ms;';
 
         this._shown = true;
@@ -679,23 +679,19 @@ export class Overview extends Signals.EventEmitter {
 
         this._changeShownState(OverviewShownState.SHOWING);
 
-        this._overview.runStartupAnimation(() => {
-            // Overview got hidden during startup animation
-            if (this._shownState !== OverviewShownState.SHOWING) {
-                callback();
-                return;
-            }
+        await this._overview.runStartupAnimation();
 
-            if (!this._syncGrab()) {
-                callback();
-                this.hide();
-                return;
-            }
+        // Overview got hidden during startup animation
+        if (this._shownState !== OverviewShownState.SHOWING)
+            return;
 
-            Main.panel.style = null;
-            this._changeShownState(OverviewShownState.SHOWN);
-            callback();
-        });
+        if (!this._syncGrab()) {
+            this.hide();
+            return;
+        }
+
+        Main.panel.style = null;
+        this._changeShownState(OverviewShownState.SHOWN);
     }
 
     getShowAppsButton() {
index 7cc481240f71ea49d832827e518444059707307d..3082ea61d56b2743b57af0fc7db0c4149d7eb2b5 100644 (file)
@@ -797,7 +797,7 @@ class ControlsManager extends St.Widget {
         this._stateAdjustment.gestureInProgress = false;
     }
 
-    async runStartupAnimation(callback) {
+    async runStartupAnimation() {
         this._ignoreShowAppsButtonToggle = true;
 
         this.prepareToEnterOverview();
@@ -839,14 +839,16 @@ class ControlsManager extends St.Widget {
         });
 
         // The Dash rises from the bottom. This is the last animation to finish,
-        // so run the callback there.
+        // so resolve the promise there.
         this.dash.translation_y = this.dash.height + this.dash.margin_bottom;
-        this.dash.ease({
-            translation_y: 0,
-            delay: STARTUP_ANIMATION_TIME,
-            duration: STARTUP_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onStopped: () => callback(),
+        return new Promise(resolve => {
+            this.dash.ease({
+                translation_y: 0,
+                delay: STARTUP_ANIMATION_TIME,
+                duration: STARTUP_ANIMATION_TIME,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onStopped: () => resolve(),
+            });
         });
     }